﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace SuperRyoninRPG.Controller
{
	/// <summary>
	/// 加速度センサーからの入力をポーリングし、静的なオブジェクトとして管理します。
	/// </summary>
	public static class Accelerometer
	{
#if WINDOWS_PHONE
        // デバイスに搭載されている加速度センサーをインスタンス化
        private static Microsoft.Devices.Sensors.Accelerometer accelerometer = new Microsoft.Devices.Sensors.Accelerometer();
        
        // ReadingChanged イベントはゲームのメイン スレッドとは別の場所で発生するため
        // 同期オブジェクトを作成します。
        private static object threadLock = new object();

        // we use this to keep the last known value from the accelerometer callback
        private static Vector3 nextValue = new Vector3();
#endif

		// 加速度センサーが 2 度初期化されるのを防ぎます
		private static bool isInitialized = false;

		// 加速度センサーが有効か
		private static bool isActive = false;

		/// <summary>
		/// 現在のゲームのために加速度センサーを初期化します。
		/// このメソッドはゲーム毎に一度だけ呼び出されます。
		/// </summary>
		public static void Initialize()
		{
			// 加速度センサーが 2 度以上初期化されようとした場合例外をスローします
			if (isInitialized)
			{
				throw new InvalidOperationException("Initialize can only be called once");
			}

#if WINDOWS_PHONE
            // 対応しているデバイスのみで加速度センサーの入力を取得します。
            // 取得に失敗した場合は例外をキャッチします。
            if (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Device)            
            {
                try
                {
                    accelerometer.ReadingChanged += new EventHandler<Microsoft.Devices.Sensors.AccelerometerReadingEventArgs>(sensor_ReadingChanged);
                    accelerometer.Start();
                    isActive = true;
                }
                catch (Microsoft.Devices.Sensors.AccelerometerFailedException)
                {
                    isActive = false;
                }
            }
            else
            {
                // エミューレーター上では加速度センサーを有効であるように設定します。
                // 方向キーで加速度センサーのシミュレーションが可能です。
                isActive = true;
            }
#endif

			// 加速度センサーが初期化済みであるというフラグを設定します。
			isInitialized = true;
		}

#if WINDOWS_PHONE
        private static void sensor_ReadingChanged(object sender, Microsoft.Devices.Sensors.AccelerometerReadingEventArgs e)
        {
            // 現在の加速度センサーの入力値を静的オブジェクトに保存し
            // 次のフレームでこの情報をゲームに反映させます。
            lock (threadLock)
            {
                nextValue = new Vector3((float)e.X, (float)e.Y, (float)e.Z);
            }
        }
#endif

		/// <summary>
		/// 加速度センサーの現在値を取得します。
		/// </summary>
		/// <returns>加速度センサーの現在値を保存した AccelerometerState</returns>
		public static AccelerometerState GetState()
		{
			// Accelerometer が初期化済みであることを確認する。
			if (!isInitialized)
			{
				throw new InvalidOperationException("You must Initialize before you can call GetState");
			}

			// ステートを保存するためのオブジェクトを新規作成する。
			Vector3 stateValue = new Vector3();

#if WINDOWS_PHONE
            // 加速度センサーが有効であるかを確認する。
            if (isActive)
            {
                if (Microsoft.Devices.Environment.DeviceType == Microsoft.Devices.DeviceType.Device)
                {
                    // デバイス上で実行中であれば加速度センサーの値を取得する。
                    lock (threadLock)
                    {
                        stateValue = nextValue;
                    }
                }
                else
                {
                    // エミューレーター上で実行中であれば、キーボートの方向キーの入力を元に
                    // 加速度の値を生成します。
                    KeyboardState keyboardState = Keyboard.GetState();

                    stateValue.Z = -1;

                    if (keyboardState.IsKeyDown(Keys.Left))
                        stateValue.X--;
                    if (keyboardState.IsKeyDown(Keys.Right))
                        stateValue.X++;
                    if (keyboardState.IsKeyDown(Keys.Up))
                        stateValue.Y++;
                    if (keyboardState.IsKeyDown(Keys.Down))
                        stateValue.Y--;

                    stateValue.Normalize();
                }
            }
#endif

			return new AccelerometerState(stateValue, isActive);
		}
	}

	/// <summary>
	/// 加速度センサーの現在のステートをカプセル化した構造体
	/// </summary>
	public struct AccelerometerState
	{
		/// <summary>
		/// 加速度センサーの現在値を g-force (重力加速度) 単位で取得する。
		/// </summary>
		public Vector3 Acceleration { get; private set; }

		/// <summary>
		/// 加速度センサーが有効であるかを取得する。
		/// </summary>
		public bool IsActive { get; private set; }

		/// <summary>
		/// AccelerometerState を初期化する。
		/// </summary>
		/// <param name="acceleration">現在の加速度値 (g-force 単位)</param>
		/// <param name="isActive">加速度センサーが有効であるか</param>
		public AccelerometerState(Vector3 acceleration, bool isActive)
			: this()
		{
			Acceleration = acceleration;
			IsActive = isActive;
		}

		/// <summary>
		/// Acceleration と IsActive プロパティーの状態を明記した文字列
		/// </summary>
		/// <returns>加速度センサーのステート情報を明記した文字列</returns>
		public override string ToString()
		{
			return string.Format("Acceleration: {0}, IsActive: {1}", Acceleration, IsActive);
		}
	}

}
